/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import Vector, {toVector} from "../../common/Vector";
import { showMathStringInTable } from "../../visual/Table2D";
import { drawDecisionTree } from "../../visual/Tree2D";
import Figure2D from "../../visual/Figure2D";

import DefautValue from "./input/TreePruning.input.json";


class CARTPruning extends AlgorithmBase{
  constructor(){
    super('CARTPruning');

    const figure = new Figure2D(600, 450);
    figure.xMax = 10.0;
    figure.xMin = 0.0;
    figure.yMax = 10.0;
    figure.yMin = 0.0;
    figure.gridX = 12;
    figure.gridY = 12;
    
    this.figure = figure;
    this.trainLog = [];

    this.alpha = 99999999999999

    this.len = 0
    this.Ct = 0 
    this.Ctt = []
    this.str = []
    this.max = 0
    this.flag = 0
    this.node_name_ = ''
    this.cmd =[]
    this.width = 0
    this.gt = []
    this.gt_name = []


  }

  init()
  {
    this.loadModel(DefautValue);

    if(this.domFigure) {
      this.domFigure.innerHTML = '';
      this.domFigure.appendChild(this.figure.domElement);
    }
    this.print();
  }

  loadModel(json){
    if(json.TrainDS){
      if(json.TrainDS instanceof Array){
        this.trainDSList = [];
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const dat = json.TrainDS[i];
          dat.x = toVector(dat.x);
          this.trainDSList.push(dat);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        let trainDS = json.TrainDS;
        this.trainDS = trainDS;
      }
    }
    
    if(json.Model){
      const model   = json.Model;
      model.w = new Vector(model.w, true);
      this.model = model;
    }
  }

  displayModel(){
    const dataSet = this.trainDS.dataSet
    const label = this.trainDS.label
    var trainLog = []

    var label_=['ID']
    for(var i=0;i<label.length;i++){
      label_.push("\\textbf{"+label[i]+"}")
    }

    trainLog.push(label_)

    for(var j=0;j<dataSet.length;j++){
      var data_=[]
      data_.push((j+1).toString())
      for(var k=0;k<dataSet[j].length;k++){
        data_.push("\\textbf{"+dataSet[j][k]+"}")
      }
      trainLog.push(data_)
    }
 
    if(this.domTest) this.domTest.innerHTML = '';
    showMathStringInTable(this.domTest, trainLog);
  }

  displayTrain(){
    const tree = this.trainDS.tree

    if(this.domTrain) this.domTrain.innerHTML = '';
    drawDecisionTree(this.domTrain, tree);
  }

  get_depth(tree){
    let that = this
    that.max = 1
    that.width = 0
    var rev = that.get_depth_(tree, that.max)
    return rev
  }
  
  get_depth_(tree, depth=1){
    let that = this
    for(var key in tree){
      var d = depth
      if((Object.keys(tree).length == 1))
      {
        if(d>that.max){
          that.max=d
        }
        that.width++
      }
      else if(key!='node'){
        d++
        that.get_depth_(tree[key], d)
      }
    }
    return {"a":that.max, "b":that.width}
  }

  cal_gini(tree, data, label){
    let that = this
    that.gini = 0
    var gini = that.cal_gini_(tree , data, label,1)
    return gini
  }
  
  cal_gini_(tree, data=[], label=[], coeff){
    let that = this
    var node = tree['node']
    var coef = coeff
    for(var key in tree){
      if(Object.keys(tree).length == 1){
        var data_2 = []
        for(var i=0;i<data.length;i++){
          if(data[i][data[i].length-1]==tree[key]){
            // console.log(data[i][data[i].length-1]);
            data_2.push(data[i])
          }
        }
        // console.log(data_2);
        // console.log(tree[key]);
        var p = data_2.length/data.length
        that.gini+= coef*(2*p*(1-p))
        // console.log(that.gini);
      }
      else if(key!='node'){
        var index_ = 0
        for(var i=0;i<label.length;i++){
          if(node == label[i]){
            index_=i
            break
          }
        }

        var data_1=[]
        for(var j=0;j<data.length;j++){
          if(data[j][index_]==key){
            data_1.push(data[j])
          }
        }
        // console.log(data_1);
        // console.log(tree[key]);
        var coef_ = data_1.length/data.length * coef
        // console.log(coef_);
        // console.log(coef);
        this.cal_gini_(tree[key],data_1,label,coef_)
      }
    }
    return that.gini
  }

  cal_cost(tree, data, label){
    let that = this
    that.len = 0
    that.Ct = 0
    var Cat = that.cal_cost_(tree , data, label)
    return Cat
  }
  
  cal_cost_(tree, data, label){
    let that = this
    var node = tree['node']
    for(var key in tree){
      if(Object.keys(tree).length == 1)
      {
        this.len++
        //找到有几类
        var know = []
        know.push(data[0])
        for(var i=1;i<data.length;i++){
          var unknown = 0
          for(var j=0;j<know.length;j++){
            if(data[i].toString()==know[j].toString()){
              unknown = 0
              break
            }
            else{
              unknown = 1
            }
          }
          if(unknown==1){
            know.push(data[i])
          }
        }
        //计算损失函数
        var know_index=[]
        for(var k=0;k<know.length;k++)
        {
          know_index[k]=0
        }
        for(var x=0;x<know_index.length;x++){
          for(var y=0;y<data.length;y++){
            if(data[y].toString()==know[x].toString())
            {
              know_index[x]++
            }
          }
        }
        var Ctk=0
        for(var z=0;z<know_index.length;z++){
          Ctk+=know_index[z]*(Math.LOG2E*Math.log(know_index[z]/data.length))
        }
        that.Ct += -Ctk
        break
      }
      if(key!='node'){
        var data_ = []
        var index = 0
        for(var i=0;i<label.length;i++){
          if(label[i]==node){
            index = i
            break
          }
        }
        for(var j=0;j<data.length;j++){
          if(data[j][index]==key){
            data_.push(data[j])
          }
        }
        that.cal_cost_(tree[key], data_, label)
      }
    }
    return that.Ct
  }

  cal_child_cost(tree, data, label, depth=1, cmd=''){
    var d = depth
    let that = this
    if(Object.keys(tree).length != 1){
      for(var key in tree){
        if(key != 'node'){
          if(Object.keys(tree[key]).length != 1){
            if(cmd.length==0){
              cmd += "new_tree['" + key + "']" 
            }
            else{
              cmd+="['" + key + "']" 
            }
            d++
            that.cal_child_cost(tree[key], data, label, d, cmd)
          }
        }
        else if(d>1){
          var new_tree = {}
          new_tree['node'] = tree['node']

          var index = 0
          for(var i=0;i<label.length;i++){
            if(tree['node'] == label[i]){
              index = i
              break
            }
          }

          var num = []
          var key_name = []
          for(var key in tree){
            if(key!='node'){
              var data_ = []
              for(var j=0;j<data.length;j++){
                if(data[j][index] == key){
                  data_.push(data[j])
                }
              }
              num.push(data_.length)
              // console.log(tree[key]);
              key_name.push(tree[key]['node'])
              var know = []
              know.push(data_[0][data_[0].length-1])
              for(var k=0;k<data_.length;k++){
                for(var x=0;x<know.length;x++){
                  if(data_[k][data_[k].length-1] != know[x]){
                    know.push(data_[k][data_[k].length-1])
                    break
                  }
                }
              }
              var know_index = []
              for(var z=0;z<know.length;z++){
                know_index.push(0)
              }
              for(var y=0;y<data_.length;y++){
                for(var a=0;a<know.length;a++){
                  if(data_[y][data_[y].length-1]==know[a]){
                    know_index[a]++
                    break
                  }
                }
              }

              var max = 0
              var name = know[0]
              for(var b=0;b<know_index.length;b++){
                if(know_index[b]>max){
                  max = know_index[b]
                  name = know[b]
                }
              }

              new_tree[key] = {'node': name}
            }
          }
          var Ct = that.cal_cost(new_tree,data,label)
          that.Ctt.push(Ct)

          var max_num = 0
          var max_key = key_name[0]
          for(var i=0;i<num.length;i++){
            if(num[i]>max_num){
              max_num = num[i]
              max_key =key_name[i]
            }
          }

          var str = cmd
          that.cmd.push(str)
          that.str.push(max_key)
        }
      }
    }
    return {"a":that.Ctt, "b":that.cmd, "c":that.str}
  }

  purn(tree,data,label){
    let that = this
    
    var CT = this.cal_cost(tree,data,label)

    that.Ctt = []
    that.cmd = []
    that.str = []
    that.gt = []
    var rev = this.cal_child_cost(tree,data,label)

    var new_tree = Object.assign({}, tree);

    var cmd = ''
    var str = ''

    for(var i=0;i<rev.a.length;i++){
      var rev_ = {}
      var str_ = "rev_ = this.get_depth(" + rev.b + ")"
      eval(str_)
      var gt = (rev.a[i]-CT)/(rev_.b - 1)
      that.gt.push(gt)
      var ud = ''
      var uds = "ud = " + rev.b
      // console.log(rev.b);
      eval(uds)
      // console.log(ud);
      that.gt_name.push(ud['node'])
      if(gt<that.alpha){
        that.alpha = gt
        cmd = rev.b[i]
        str = rev.c[i]
      }
    }
    if(cmd!=''){
      var s = "delete "+ cmd
      var s_ = cmd + " = {'node' : '" + str + "'}"
      eval(s)
      eval(s_)
    }
    return new_tree
  }

  train(){
    const dataSet = this.trainDS.dataSet
    const label = this.trainDS.label

    var tree = this.trainDS.tree
    let that = this

    var T = []
    T.push(tree)

    var trainlog = []

    trainlog.push(["\\text{迭代次数}","\\text{内部结点}","g_t","a","\\text{是否剪枝}"])

    var count = 1
    
    while(that.get_depth(tree).a>2){
      var new_ = that.purn(tree,dataSet,label)
      if(JSON.stringify(tree) === JSON.stringify(new_)){
        break
      }
      else{
        T.push(new_)
        tree = Object.assign({}, new_);
        for(var i=0;i<that.gt.length;i++){
          if(that.gt[i]!=that.alpha){
            trainlog.push([`${count}`, `${that.gt_name[i]}`, `${that.gt[i]}`, `${that.alpha}`, "\\text{否}"])
          }
          else{
            trainlog.push([`${count}`, `${that.gt_name[i]}`, `${that.gt[i]}`, `${that.alpha}`, "\\text{是}"])
          }
        }
      }
      count++
    }

    console.log(T);
    var min = 99999999999999
    var index_t = 0
    for(var i=0;i<T.length;i++){
      var gini = that.cal_gini(T[i],dataSet,label)
      console.log(gini);
      if(gini<min){
        min = gini
        index_t = i
      }
    }

    // var end_tree = Object.assign({}, T[index_t]);

    if(this.domModel) this.domModel.innerHTML = '';
    drawDecisionTree(this.domModel, tree);
    if(this.domTable) this.domTable.innerHTML = '';
    showMathStringInTable(this.domTable, trainlog);
    

  }

  print(){
    this.figure.clearAll();
    this.displayModel();
    this.displayTrain();
  }
}


export default CARTPruning;